home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 20 code / NetWare Development / ATDemo / ATDemo.c next >
Encoding:
C/C++ Source or Header  |  1994-10-06  |  15.9 KB  |  506 lines  |  [TEXT/MPS ]

  1. /*
  2.  *  This sample program awaits an ATP connection from a client.  When it gets one
  3.  *   it spins off a new thread to handle the conversation with the client, which
  4.  *   can include an arbitrary number of queries regarding certain server information.
  5.  *   The server can handle up to 128 simultaneous clients.
  6.  *
  7.  *  This code is based on the ATPSERV code provided in the Novell NLM SDK
  8.  *
  9.  *  Written by: Jamie Osborne   9/94
  10.  *
  11.  *  You may copy and modify this code at will.
  12.  *
  13.  *  Please note that this NLM is intended at a demonstration program.  At the time
  14.  *  of its creation, NetWare for PowerPC was in alpha.  As such, this program
  15.  *  may not compile and run without changes in the final product.
  16.  *
  17.  * Apple Computer, Inc. makes no warranties about the the fitness of this code
  18.  * for any purpose and will not be held liable for any damages you may suffer
  19.  * from using this code.  Furthermore, Apple Computer, Inc. will not be held
  20.  * liable for the death of Elvis or any other space alien.
  21.  *
  22.  * Your mileage may vary.
  23.  */
  24.  
  25. // Some standard includes
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <errno.h>
  29. #include <string.h>
  30. #include <nwenvrn.h> 
  31. #include <signal.h>
  32.  
  33. /* You can compile this file with the Watcom C32 compiler to build
  34.     it for NetWare running on an Intel-type machine.  However,
  35.     at the time this program was written, the NetWare interfaces
  36.     for Intel had not been updated to 4.1.
  37. */
  38.  
  39. #ifdef OLD_INTERFACES
  40. #include <process.h>
  41. #include <conio.h>
  42. #else
  43. #include <nwthread.h>
  44. #include <nwerrno.h> 
  45. #endif
  46.  
  47. // These includes are all necessary for using Apple Transaction Protocol
  48. #include <appletlk.h>
  49. #include <atp.h>
  50. #include <nbp.h>
  51. #include <ddp.h>
  52. #include <zip.h>
  53.  
  54. /* Globals   */
  55. ATEntity_t thisEntity;                  // The server's NBP entity
  56. int     threadCount = 0;                // A count of the number of spawned threads
  57. int     exitingNLM = FALSE;             // Flag set when the user unloads the NLM
  58. int     *my_fd;                         // The main fd used for listening for new connections
  59.  
  60. // We just prototype a few functions here instead of making a .h file
  61. extern int advertise(int fd,char *nbp_name);                            // Advertises our server with NBP
  62. void CleanUpFunc( int );                                                // Called on a Signal
  63. void HandleClientSession(int *client_fd);                               // Handles query/response with a client
  64. int SpinNewSession(ATAtpPass_t *req_pass);                              // Creates a new thread for a client connection
  65. int HandleRequest(char *input, char *output, int reqNum);               // Interprets client requests
  66.  
  67. #define kServerType     ":ATDEMO_Server.NATIVE@"
  68. enum {STREAM_ERROR = -1};
  69. enum {kRequestedName = 2, kRequestedConnectionsInUse, kRequestedPeakConnections, kRequestedQuit};
  70.  
  71.  
  72. void main()
  73. {
  74.     ATSocket        sock = 0;                               // The server's AppleTalk socket. 
  75.     int             err = 0;   
  76.     int             flags = 0;
  77.     char            myType[52] = kServerType;               // NBP type
  78.     char            myServer[128];                          // Server name.
  79.     int             completionCode;
  80.     ATAtpPass_t rcv_pass;                                   // Used for incoming requests
  81.     ATInet_t        info;                                   // The server's AppleTalk internet address.
  82.     ATNvestr_t      myZone;                                 // The server's zone.
  83.     ATInet_t        router_info;                            // The router's AppleTalk internet address (if there if a router).
  84.  
  85. //  printf will print to the screen automatically created for the NLM when it is loaded
  86.     printf(  "AppleTalk Demo Server\n");
  87.  
  88. //  Register handlers for these various signals     
  89.     signal (SIGINT,CleanUpFunc);
  90.     signal (SIGABRT,CleanUpFunc);
  91.     signal (SIGTERM,CleanUpFunc);
  92.  
  93. //  Allocate the file descriptor used for incoming connections
  94.     my_fd = (int *)malloc(sizeof(int));
  95.  
  96. //  Open a socket dynamically
  97.     if( err = ATAtpOpen(my_fd,&sock) )
  98.     {
  99.          printf("ATAtpOpen failed: err = %x\n",err);
  100.          exit(__LINE__);                // Abort
  101.     }
  102.     
  103.  
  104. //  Get our address from the router
  105.     if(err = ATDdpNetinfo(*my_fd,&info,&router_info,&flags) )
  106.     {
  107.         printf("ATDdpNetinfo failed: err = %x\n", err);
  108.         ATAtpClose(*my_fd);
  109.         free(my_fd);
  110.         exit(__LINE__);
  111.     }
  112.     
  113. // Add "*" as the zone name.
  114.    strncat(myType,"*",1);
  115.  
  116.  
  117. //  Register the server's name and advertise it on the internet.
  118.     GetFileServerName(0, myServer);
  119.     strcat( myServer, myType );             // Concatenate our NBP type onto the name
  120.     
  121.     if (advertise( *my_fd, myServer ) == STREAM_ERROR)
  122.     {
  123.         printf("Advertise function returned Stream Error");
  124.         ATAtpClose(*my_fd);
  125.         free(my_fd);
  126.         exit(__LINE__);
  127.     }
  128.  
  129. //  Print our AppleTalk address to the screen
  130.     printf("%s's AppleTalk address is: (%d.%d.%d)\n", myServer,info.net,info.node,info.socket);
  131.  
  132. //  Get the AppleTalk zone the server lives in
  133.     if(err = ATZipGetMyZone(&myZone) )
  134.     {
  135.         printf("ATZipGetMyZone failed: err = %x\n",err);
  136.         ATAtpClose(*my_fd);
  137.         free(my_fd);
  138.         exit(__LINE__);
  139.     }
  140.  
  141.  
  142.     myZone.str[myZone.len] = 0; // NULL terminate the string. (AppleTalk silliness)
  143.     
  144.     printf("The server is registered in the zone name: %s\n",myZone.str);   
  145.     printf("Waiting for request...\n\n");
  146.  
  147. //  Allcate memory for the receive buffer.
  148. //  If this is not done, the server will crash when you receive a connection
  149.     rcv_pass.data = (void *)malloc(ATP_DATA_SIZE);
  150.     rcv_pass.data_len = ATP_DATA_SIZE;      
  151.  
  152. //  Begin our loop waiting for initial connections
  153.     do{
  154.     
  155. /*      This is the main point where our program will spend most of its time.
  156.         The main thread for this NLM will block here until it receives an
  157.         ATP request directed at our server
  158. */
  159.         if(err = ATAtpGet(*my_fd,&rcv_pass) )
  160.         {                
  161.             printf("ATAtpGet failed: err = %x\n",err);
  162.             exitingNLM = TRUE;
  163.             break;
  164.         }
  165.  
  166. //      Check the return error from the packet.  This is a protocol error
  167.         if(rcv_pass.ret)
  168.             printf("rcv_pass.ret returned error (%x)\n",rcv_pass.ret);
  169.  
  170. //      Print the AppleTalk address of the sender
  171.         printf("Received message from a client at %u.%u.%u.\n",
  172.                 rcv_pass.at_addr.net,
  173.                 rcv_pass.at_addr.node,
  174.                 rcv_pass.at_addr.socket);
  175.  
  176. //      Print the message from the client
  177.         printf("Received:%s\n",rcv_pass.data);
  178.  
  179. //      Spin off a new thread to handle this session            
  180.         err = SpinNewSession(&rcv_pass);
  181.         if (err == EFAILURE)
  182.         {
  183.             printf("SpinNewSession failed: err = %x\n",errno);
  184.             exitingNLM = TRUE;
  185.         }
  186.       
  187.     }while (!exitingNLM);
  188.  
  189.     ATAtpClose(*my_fd);
  190.     free(my_fd);
  191.     free(rcv_pass.data);
  192. }
  193.  
  194.  
  195. /* SpinNewSession is called by main().  It opens a new socket,
  196.     creates a new thread, and tells the client where (to which socket)
  197.     it should send all of its requests.  The new thread then
  198.     handles the requests, allowing the main loop to await new ones.
  199. */
  200. int SpinNewSession(ATAtpPass_t *rcv_pass)
  201. {
  202.     ATSocket        sock = 0;                       // The new socket
  203.     int             err;
  204.     int             *new_fd;                        // The new file descriptor for the socket
  205.     int                     threadID;                       // Thread ID of the new thread
  206.     ATAtpPass_t resp_pass;                  // For sending our first response
  207.     char            respBuffer[256];        
  208.     int             completionCode;
  209.     
  210.     printf("In SpinNewSession...\n");
  211.  
  212. //  Allocate room for a new file descriptor
  213.     new_fd = (int *)malloc(sizeof(int));
  214.  
  215. //  Open a new socket for this client only  
  216.     if( err = ATAtpOpen(new_fd,&sock) )
  217.     {
  218.          printf("ATAtpOpen failed: err = %x\n",err);
  219.          free(new_fd);
  220.          return EFAILURE;
  221.     }
  222.     printf("Opened socket: %d, fd :%d\n", sock, *new_fd);
  223.     
  224. //  Send back a response, including which socket the client
  225. //  should send further requests to
  226.     threadID = GetThreadID();
  227.     sprintf(respBuffer, "You are being handled by socket:%d", sock);
  228.  
  229.     resp_pass.at_addr = rcv_pass->at_addr;          // The destination is the rcv source
  230.  
  231. //  Just to keep the console informed
  232.     printf("Sending response to client at %u.%u.%u.\n",
  233.     resp_pass.at_addr.net,
  234.     resp_pass.at_addr.node,
  235.     resp_pass.at_addr.socket);
  236.  
  237.  
  238.     resp_pass.bitmap = 1;                           // We're sending 1 packet
  239.     resp_pass.packetize = 0;                        // No need to packetize
  240.     resp_pass.retry.interval = 2000;                // 2000 ticks to retry
  241.     resp_pass.retry.retries = 10;
  242.     resp_pass.retry.backoff = 2;
  243.     resp_pass.ret =0;
  244.     resp_pass.data = respBuffer;                    // Our string
  245.     resp_pass.data_len = strlen(respBuffer)+1;      // The string's length plus on for the NULL
  246.     resp_pass.userdata[0] = sock;                   // *** Store the new socket in the user data.  A handy place for ints
  247.     resp_pass.xo = 1;                               // We want this sent exactly once
  248.     resp_pass.xo_relt = ATP_XO_DEF_REL_TIME;
  249.     resp_pass.TransID = rcv_pass->TransID;          // Match the TID from the client
  250.     
  251. //  Send back our first response on the old file descriptor
  252.     printf("Sending first thread response on old fd %d.\n", *my_fd);
  253.     if( err = ATAtpSendRsp(*my_fd,&resp_pass))
  254.     {
  255.          printf("ATAtpRsp failed: err = %x\n",err);
  256.          free(new_fd);
  257.          return EFAILURE;
  258.     }
  259.  
  260. //  Begin a new thread, allowing it to create its own stack.        
  261.     completionCode = BeginThread(HandleClientSession, NULL, 0, new_fd);
  262.     if (completionCode != ENOMEM && completionCode != EINVAL)
  263.         threadCount++;          // Increment our global thread counter
  264.  
  265.     return completionCode;          
  266. }
  267.  
  268.  
  269. /*  HandleClientSession is called by the thread that is created by
  270.     SpinNewSession.  It is similar to our main loop, but it handles
  271.     interaction with a single client.  When the client tells us its
  272.     finished, or the some abnormal condition happens, we terminate
  273.     the thread.  Note that if the function returns, the thread is
  274.     terminated by the system
  275. */
  276. void HandleClientSession(int *client_fd)
  277. {
  278.     ATAtpPass_t     rcv_pass, resp_pass;              // For receiving and responding
  279.     int                     err, quitRequest;       // quitRequest is used to determine if the client is done
  280.     char                    respBuffer[128];        // Buffer for our response strings
  281.     
  282. //  Allocate room for our receive buffer
  283.     rcv_pass.data = (void *)malloc(ATP_DATA_SIZE);
  284.     rcv_pass.data_len = ATP_DATA_SIZE;
  285.  
  286.     do
  287.     {
  288. //      This Get will block until our client sends a request for information.
  289. //      If we never get a request, this call never returns
  290.         printf("In client handling thread: Calling ATAtpGet fd:%d.\n", *client_fd);
  291.         if(err = ATAtpGet(*client_fd,&rcv_pass) )
  292.         {
  293.          
  294.              printf("ATAtpGet failed: err = %x\n",err);
  295.              ATAtpClose(*client_fd);                // We own the fd when we're called
  296.              free(rcv_pass.data);
  297.              free(client_fd);
  298.              threadCount--;
  299.              return;                                // This will kill the thread
  300.         }
  301.  
  302. //      The get was successful, but there was a protocol error.
  303.         if(rcv_pass.ret)
  304.              printf("rcv_pass.ret returned error (%x)\n",rcv_pass.ret);
  305.  
  306. //      Show who the message was from
  307.         printf("Received message from a client at %d.%d.%d.\n",
  308.         rcv_pass.at_addr.net,
  309.         rcv_pass.at_addr.node,
  310.         rcv_pass.at_addr.socket);
  311.         
  312. //      Print what we got
  313.         printf("Received:%s\n",rcv_pass.data);
  314.         printf("Userdata: %d\n", rcv_pass.userdata[0]);   
  315.  
  316. //      Interpret the request and get the appropriate response in the response buffer
  317. //      Requests are sent as integers in the userdata field
  318.         quitRequest = HandleRequest((char *)rcv_pass.data, &respBuffer, rcv_pass.userdata[0]);
  319.         
  320. //      Send back a response
  321.         resp_pass.at_addr = rcv_pass.at_addr;
  322.         resp_pass.bitmap = 1;/* Sending 1 packet. */
  323.         resp_pass.packetize = 0;
  324.         resp_pass.retry.interval = 2000;
  325.         resp_pass.retry.retries = 2;
  326.         resp_pass.retry.backoff = 2;
  327.         resp_pass.ret =0;
  328.         resp_pass.data = respBuffer; 
  329.         resp_pass.data_len = strlen(respBuffer)+1;
  330.         resp_pass.userdata[0] = 0;
  331.         resp_pass.xo = 1;
  332.         resp_pass.xo_relt = ATP_XO_DEF_REL_TIME;
  333.         resp_pass.TransID = rcv_pass.TransID;
  334.  
  335.     
  336.         printf("Sending thread response on fd %d.\n", *client_fd);
  337.         if( err = ATAtpSendRsp(*client_fd,&resp_pass))
  338.         {
  339.              printf("ATAtpRsp failed: err = %x\n",err);
  340.              ATAtpClose(*client_fd);
  341.              free(rcv_pass.data);
  342.              free(client_fd);
  343.              threadCount--;
  344.              return;
  345.         }
  346.  
  347.         if (quitRequest)
  348.         {
  349.             printf("Client requested quit\n");
  350.             ATAtpClose(*client_fd);
  351.             free(rcv_pass.data);
  352.             free(client_fd);
  353.             threadCount--;
  354.             return;
  355.         }       
  356.     }while ( !exitingNLM );
  357.  
  358.  
  359. //  We will only reach this point if exitingNLM is true.  That means someone unloaded us
  360. //  at the console
  361.  
  362. //  Close the ATP socket gracefully.
  363.     if(err = ATAtpClose(*client_fd))
  364.     {
  365.          printf("ATAtpClose failed: err = %x\n", err);
  366.     }       
  367.     
  368.     free(rcv_pass.data);
  369.     free(client_fd);               
  370.     threadCount--;
  371. }
  372.  
  373. /* HandleRequest interprets the the request number supplied and gets the 
  374.     appropriate answer.  If the client requests to quit, the function returns 1,
  375.     otherwise it returns 0.
  376. */
  377. int HandleRequest(char *input, char *output, int reqNum)
  378. {
  379.     int                     ccode, structsize = 128;        // For calls to get server info
  380.     FILE_SERV_INFO  sbuf;                                           // Server info data structure
  381.     int                             requestedQuit = 0;
  382.     
  383. //  Get some generic information about the current server status.
  384.     ccode = GetServerInformation(structsize, &sbuf);
  385.     if (ccode != 0)
  386.     {
  387.         printf("GetServerInformation returned an error! %d.\n", ccode);
  388.         return 1;       // this is OK even if ccode is 1.  We just want to kill this thread
  389.     }
  390.     
  391. //  Print the request number
  392.     printf("ReqNum: %d\n", reqNum);
  393.  
  394. //  Interpret it
  395.     switch(reqNum)
  396.     {
  397.         case kRequestedName:
  398.             strcpy(output, sbuf.serverName);                
  399.         break;
  400.         case kRequestedConnectionsInUse:
  401.             sprintf(output,"%d", sbuf.connectionsInUse);
  402.         break;
  403.         case kRequestedPeakConnections:
  404.             sprintf(output,"%d", sbuf.peakConnectionsUsed);
  405.         break;
  406.         case kRequestedQuit:
  407.                 strcpy(output, "Bye bye client.");
  408.                 requestedQuit = 1; 
  409.         break;
  410.         default:
  411.             strcpy(output, "Unknown request.");             
  412.         break;
  413.     }
  414.     
  415.     printf("In HandleRequest: input: %s output: %s\n", input, output);
  416.  
  417.     return requestedQuit;
  418. }
  419.  
  420.  
  421.  
  422.  
  423. /*  The advertise() function advertises the server's name on the network. The
  424. function parses the name string ServerName:ATP_Server.NATIVE@zonename
  425. into an nbp name with a type, object, and zone.  Then, it
  426. registers the name with the internet.  */
  427. int advertise( int fd, char * nbp_name)
  428. {
  429.     int err;
  430.     ATRetry_t retry;
  431.     
  432.     /*
  433.     * Set up the structure that holds the timeout and retries.
  434.     */
  435.     
  436.     retry.interval = 1000; /* Timeout Value. */
  437.     retry.retries  = 8; /* Number of retries.*/
  438.     retry.backoff = 2;  /* Gradually increases in time between retries. */
  439.     
  440.     if (err = ATNbpParseEntity(&thisEntity, nbp_name))
  441.     {
  442.          printf("ATNbpParseEntity failed: err = %x\n",err);
  443.          return STREAM_ERROR;   /* Clean up and exit is done in main. */
  444.     }
  445.     if (err = ATNbpRegister( &thisEntity, fd, (ATRetry_t *)&retry))
  446.     {
  447.          printf("ATNbpRegister failed: err = %x\n",err);
  448.          return STREAM_ERROR;   /* Clean up and exit is done in main. */
  449.     }
  450.     
  451.     return 0;
  452. }
  453.  
  454. /*
  455.     CleanUpFunc is registered at the beginning of our program.  If
  456.     any of these signal conditions happen, the appropriate code will
  457.     be executed.  If the NLM is unloaded at the console, SIGTERM is
  458.     raised.
  459. */
  460. void CleanUpFunc(int signalCondition)
  461. {
  462.     int err;
  463.     int i = 0;
  464.     
  465.     switch(signalCondition)
  466.     {
  467.        case (SIGTERM) :
  468.         ConsolePrintf("Unload Detected in CleanUp Function!\n\r") ;
  469.         exitingNLM = TRUE;
  470.         
  471.             ConsolePrintf("Calling ATNbpRemove...\n");
  472.             if(err = ATNbpRemove(&thisEntity,*my_fd))
  473.             {
  474.                  printf("ATNbpRemove failed: err = %x\n",err);
  475.                  exit(__LINE__);
  476.             }
  477.  
  478.  
  479. //          Close the ATP socket gracefully.                
  480.             ConsolePrintf("Calling ATAtpClose on %d\n", *my_fd);
  481.             if(err = ATAtpClose(*my_fd))
  482.             {
  483.                  printf("ATAtpClose failed: err = %x\n", err);
  484.                  exit(__LINE__);
  485.             }       
  486.  
  487. //      Give the various threads a chance to clean up
  488. //      If it hasn't happened in 300 thread switches, give up.
  489.         while(threadCount > 0 & i++ < 300)
  490.         {
  491.             ConsolePrintf("ThreadCount: %d\n", threadCount);
  492.                 ThreadSwitchWithDelay() ;
  493.         }
  494.   
  495. //      Gime main() one last chance to clean up
  496.         ThreadSwitchWithDelay() ;
  497.     break ;
  498.     case (SIGABRT) :
  499.     case (SIGINT) :
  500.             exit(__LINE__);
  501.     break ;
  502.     default :
  503.         ConsolePrintf("Unknown Signal Condition\n\r") ;
  504.     }
  505. }
  506.